﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using EnetaMvc.ApplicationServices.Dto;
using EnetaMvc.Core.Domain.Common;
using EnetaMvc.Core.Domain.Content;
using EnetaMvc.Core.Domain.Events;
using EnetaMvc.Core.Repositories;
using EnetaMvc.Infrastructure;

namespace EnetaMvc.ApplicationServices
{
    public class EventService : IEventService
    {
        private readonly IEventRepository _eventRepository;
        private readonly IUserRepository _userRepository;
        private readonly IPublishStateRepository _publishStateRepository;
        private readonly ITagRepository _tagRepository;

        public EventService(IEventRepository eventRepository, 
                            IUserRepository userRepository, 
                            IPublishStateRepository publishStateRepository,
                            ITagRepository tagRepository)
        {
            _eventRepository = eventRepository;
            _userRepository = userRepository;
            _publishStateRepository = publishStateRepository;
            _tagRepository = tagRepository;
        }

        public PagedResult<EventListDto> ListPublicEvents(int page, int pageSize)
        {
            var events = _eventRepository.ListPublicEvents(page, pageSize);
            var dtoResult = GetEventDtoList(events);

            return dtoResult;
        }

        public PagedResult<EventListDto> ListAllEvents(int page, int pageSize)
        {
            var events = _eventRepository.ListEvents(page, pageSize);
            var dtoResult = GetEventDtoList(events);

            return dtoResult;
        }

        private static PagedResult<EventListDto> GetEventDtoList(PagedResult<Event> events)
        {
            var eventDtos = new List<EventListDto>();
            var dtoResult = new PagedResult<EventListDto>();

            Mapper.CreateMap<Event, EventListDto>();
            Mapper.Map(events.Results, eventDtos);

            Mapper.CreateMap(events.GetType(), dtoResult.GetType())
                  .ForMember("Results", m => m.Ignore());
            Mapper.Map(events, dtoResult);

            dtoResult.Results = eventDtos;
            return dtoResult;
        }

        public EventDto GetEventById(long id)
        {
            var evt = _eventRepository.GetEventById(id);
            if (evt == null)
                return null;

            var evtDto = new EventDto();

            Mapper.CreateMap<Tag, TagDto>();
            Mapper.CreateMap<MyRole, MyRoleDto>();
            Mapper.CreateMap<Comment, CommentDto>();

            Mapper.CreateMap<EventAttendee, EventAttendeeDto>()
                  .ForMember(v => v.UserName, v => v.MapFrom(e => e.User.UserName))
                  .ForMember(v => v.UserId, v => v.MapFrom(e => e.User.Id));

            Mapper.CreateMap<Event, EventDto>()
                  .ForMember(v => v.AttendeesAtRealEvent, v => v.MapFrom(e => e.Attendees.Where(a => a.OnRealEvent).ToArray()))
                  .ForMember(v => v.AttendeesAtVirtualEvent, v => v.MapFrom(e => e.Attendees.Where(a => !a.OnRealEvent).ToArray()))
                  .ForMember(v => v.Comments, v => v.MapFrom(e => e.Comments.ToArray()))
                  .ForMember(v => v.Tags, v => v.MapFrom(e => e.Tags.ToArray()))
                  .ForMember(v => v.TargetAudiences, v => v.MapFrom(e => e.MyRoles.ToArray()));

            Mapper.Map(evt, evtDto);

            return evtDto;
        }

        public void SaveEvent(EventDto eventDto)
        {
            var evt = _eventRepository.GetEventById(eventDto.Id);
            if (evt == null)
                evt = new Event();

            Mapper.CreateMap<EventDto, Event>()
                  .ForMember(m => m.Attendees, m => m.Ignore())
                  .ForMember(m => m.Comments, m => m.Ignore())
                  .ForMember(m => m.CreatedBy, m => m.Ignore())
                  .ForMember(m => m.PublishState, m => m.Ignore())
                  .ForMember(m => m.PublishStateBy, m => m.Ignore())
                  .ForMember(m => m.PublishStateDate, m=>m.Ignore())
                  .ForMember(m => m.Created, m=>m.Ignore())
                  .ForMember(m => m.Tags, m => m.Ignore());
            Mapper.Map(eventDto, evt);

            if (evt.Id == 0)
            {
                evt.Created = DateTime.Now;
                evt.CreatedBy = _userRepository.GetUserById(eventDto.CreatedById);
            }

            if (evt.PublishState == null || evt.PublishState.Id != eventDto.PublishStateId)
            {
                evt.PublishStateDate = DateTime.Now;
                evt.PublishStateBy = _userRepository.GetUserById(eventDto.PublishStateById);
                evt.PublishState = _publishStateRepository.GetPublishStateById(eventDto.PublishStateId);
            }

            if (eventDto.SaveTagIds != null)
            {
                evt.Tags.Clear();
                foreach (var tagId in eventDto.SaveTagIds)
                {
                    var tag = _tagRepository.GetTagById(tagId);
                    evt.Tags.Add(tag);
                }
            }

            if (eventDto.SaveAudienceIds != null)
            {
                evt.MyRoles.Clear();
                foreach (var myRoleId in eventDto.SaveAudienceIds)
                {
                    var role = _userRepository.GetMyRoleById(myRoleId);
                    evt.MyRoles.Add(role);
                }
            }

            _eventRepository.SaveEvent(evt);
        }


        public IList<EventAttendeeDto> GetUserAttendings(long userId)
        {
            var attendings = _eventRepository.GetUserAttendings(userId);
            var attendingDtos = new List<EventAttendeeDto>();

            if (attendings == null)
                return attendingDtos;

            Mapper.CreateMap<EventAttendee, EventAttendeeDto>();
            Mapper.Map(attendings, attendingDtos);

            return attendingDtos;
        }

        public bool IsAttendeeOfEvent(long eventId, long userId)
        {
            var attendings = _eventRepository.GetUserAttendings(userId);
            return (attendings.Where(a => a.Event.Id == eventId).Count() > 0);
        }

        public void RegisterUserToEvent(long eventId, long userId, bool realEvent)
        {
            var evt = _eventRepository.GetEventById(eventId);
            if (evt == null)
                return;
            if (evt.RegistrationEndDate < DateTime.Now)
                return;
            if (evt.Seats <= evt.Attendees.Count)
                return;
            if (IsAttendeeOfEvent(eventId, userId))
                return;

            var attendee = new EventAttendee();
            attendee.Event = evt;
            attendee.OnRealEvent = realEvent;
            attendee.RegistrationDate = DateTime.Now;
            attendee.User = _userRepository.GetUserById(userId);

            _eventRepository.SaveAttendee(attendee);
        }

        public void UnregisterFromEvent(long eventId, long userId)
        {
            var evt = _eventRepository.GetEventById(eventId);
            if (evt == null)
                return;

            if (evt.RegistrationEndDate < DateTime.Now)
                return;

            var attendee = evt.Attendees.FirstOrDefault(a => a.User.Id == userId);
            if (attendee == null)
                return;

            _eventRepository.DeleteAttendee(attendee);
        }

        public void DeleteEvent(long id)
        {
            var evt = _eventRepository.GetEventById(id);
            if (evt == null)
                return;

            _eventRepository.DeleteEvent(evt);
        }
    }
}